package datarunner

import (
	"gamesdk/common/log"
	"sync"
	"time"
)

type DataSource interface {
	Load() map[interface{}]interface{}
	Update(key, val interface{})
	Delete(key interface{})
}

type DataRunner struct {
	opt   options
	pool  *sync.Map
	ds    DataSource
	close chan bool
}

func (this *DataRunner) Store(key, data interface{}) {
	this.pool.Store(key, data)
	this.ds.Update(key, data)
}

func (this *DataRunner) Delete(key interface{}) {
	this.pool.Delete(key)
	this.ds.Delete(key)
}

func (this *DataRunner) Reload() { this.reload() }

func (this *DataRunner) Load(key interface{}) (interface{}, bool) {
	return this.pool.Load(key)
}

// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
func (this *DataRunner) Range(f func(key, value interface{}) bool) {
	this.pool.Range(f)
}

func (this *DataRunner) Exit() {
	close(this.close)
}

func NewDataRunner(ds DataSource, opts ...Option) *DataRunner {
	dr := new(DataRunner)
	dr.close = make(chan bool, 0)
	dr.opt = options{
		of: false,
		tf: false,
	}
	for _, opt := range opts {
		opt.apply(&dr.opt)
	}
	dr.ds = ds
	if dr.opt.tf {
		go dr.tf()
	}
	if dr.opt.of {
		go dr.of()
	}
	dr.reload()
	return dr
}

func (this *DataRunner) reload() {
	list := this.ds.Load()
	pool := new(sync.Map)
	for k, v := range list {
		pool.Store(k, v)
	}
	this.pool = pool
}

//根据指定时间计算下一个时间差距
func getNextTime(h, m, s int) time.Duration {
	now := time.Now()
	//算出当天运行时间
	target := now.Add(-time.Hour*time.Duration(now.Hour()) -
		time.Minute*time.Duration(now.Minute()) -
		time.Second*time.Duration(now.Second()) -
		time.Nanosecond*time.Duration(now.Nanosecond()))
	target = target.Add(time.Hour*time.Duration(h) +
		time.Minute*time.Duration(m) +
		time.Second*time.Duration(s))
	//今天运行
	if target.After(now) {
		return target.Sub(now)
	}
	return target.AddDate(0, 0, 1).Sub(now)
}

func (this *DataRunner) tf() {
	timer := time.NewTimer(this.opt.timer)
	for {
		select {
		case <-timer.C:
			this.reload()
			timer.Reset(this.opt.timer)
		case <-this.close:
			break
		}
	}
}

func (this *DataRunner) of() {
	var timer *time.Timer
	for {
		dur := getNextTime(this.opt.hour, this.opt.min, this.opt.sec)
		log.Info("next time %s", dur.String())
		timer = time.NewTimer(dur)
		select {
		case <-timer.C:
			this.reload()
		case <-this.close:
			break
		}
	}
}
